package io.github.eric.urpc.client.handler;

import io.github.eric.urpc.client.ClientContext;
import io.github.eric.urpc.core.conn.Connection;
import io.github.eric.urpc.core.rpc.msg.HeartBeat;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;

import java.util.concurrent.CountDownLatch;

public class ClientConnectionHandler extends ClientBaseHandler {
	private final CountDownLatch channelActiveLatch = new CountDownLatch(1);

	public ClientConnectionHandler(Bootstrap bootstrap, ClientContext clientContext) {
		super(bootstrap, clientContext);
	}

	@Override
	public void channelRegistered(final ChannelHandlerContext ctx) {
		if (clientContext.getLogger().isDebugEnabled()) {
			clientContext.getLogger().debug("[urpc client] channel {} is registered, local addr {}, remote addr {}",
					ctx.channel().id().asShortText(), ctx.channel().localAddress(), ctx.channel().remoteAddress());
		}
		Connection conn = new Connection(ctx.channel(), clientContext);
		ctx.channel().attr(Connection.TCP).set(conn);
	}

	@Override
	public void channelActive(final ChannelHandlerContext ctx) {
		if (clientContext.getLogger().isDebugEnabled()) {
			clientContext.getLogger().debug("[urpc client] channel {} is active, local addr {}, remote addr {}",
					ctx.channel().id().asShortText(), ctx.channel().localAddress(), ctx.channel().remoteAddress());
		}
		ctx.channel().attr(Connection.TCP).get().onConnected();
		channelActiveLatch.countDown();
	}

	//对端主动调用close，都只会触发Inactive，不会导致抛异常
	@Override
	public void channelInactive(final ChannelHandlerContext ctx) {
		if (clientContext.getLogger().isDebugEnabled()) {
			clientContext.getLogger().debug("[urpc client] channel {} is inactive, local addr {}, remote addr {}",
					ctx.channel().id().asShortText(), ctx.channel().localAddress(), ctx.channel().remoteAddress());
		}
		ctx.channel().attr(Connection.TCP).get().onDisconnected();
		ctx.close();
	}

	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
		if (evt instanceof IdleStateEvent) {
			IdleState state = ((IdleStateEvent) evt).state();
			if (state == IdleState.WRITER_IDLE) {
				clientContext.getHeartBeatExecutor().execute(() -> {
					if (ctx != null && ctx.channel() != null && ctx.channel().isActive()) {
						ctx.channel().writeAndFlush(new HeartBeat());
//						if (clientContext.getLogger().isDebugEnabled()) {
							clientContext.getLogger().info("[urpc client] channel {} write idle, send a heart beat to urpc server {}",
									ctx.channel().id().asShortText(), ctx.channel().remoteAddress());
//						}
					}
				});
			}
		} else {
			ctx.fireUserEventTriggered(evt);
		}
	}

	//对端被动关闭连接（如关闭进程），则先抛出异常，而后触发Inactive
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		// Close the connection when an exception is raised.
		clientContext.getLogger().error("[urpc client] channel {} exception caught, local addr {}, remote addr {}",
				ctx.channel().id().asShortText(), ctx.channel().localAddress(), ctx.channel().remoteAddress(), cause);
		ctx.close();
	}

	public CountDownLatch getChannelActiveLatch() {
		return channelActiveLatch;
	}
}
